home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / nos042_s / mailbox.c < prev    next >
C/C++ Source or Header  |  1994-09-16  |  51KB  |  2,006 lines

  1. /* There are only two functions in this mailbox code that depend on the
  2.  * underlying protocol, namely mbx_getname() and dochat(). All the other
  3.  * functions can hopefully be used without modification on other stream
  4.  * oriented protocols than AX.25 or NET/ROM.
  5.  *
  6.  * SM0RGV 890506, most work done previously by W9NK
  7.  *
  8.  *** Changed 900114 by KA9Q to use newline mapping features in stream socket
  9.  *    interface code; everything here uses C eol convention (\n)
  10.  *
  11.  *    Numerous new commands and other changes by SM0RGV, 900120
  12.  *
  13.  *    14 Mar 92    GT    Fix declaration syntax error under BC++ 3.00.
  14.  *    25 Jun 92    paul@wolf.demon.co.uk  Make privilege check for
  15.  *                    external email from mailbox
  16.  *    25 Jun 92    paul@wolf.demon.co.uk  Add message if entering mailbox
  17.  *                    with an '@' password.
  18.  *
  19.  ****************************************************************************
  20.  *    $Id: mailbox.c 1.6 94/02/02 14:38:17 ROOT_DOS Exp $
  21.  *    08 May 93    1.5        GT    Fix warnings.        
  22.  *    01 Feb 94    1.6        GT    New sendfile prototype.
  23.  *
  24.  * ST NOS version by David Nash
  25.  *
  26.  ***************************************************************************/
  27.  
  28. #include <stdio.h>
  29. #include <time.h>
  30. #include <ctype.h>
  31. #include <errno.h>    /* ST NOS - sys_errlist > strerror    */
  32. #ifdef    UNIX
  33. #include <sys/types.h>
  34. #include <sys/stat.h>
  35. #endif
  36.  
  37. #include "global.h"
  38. #include "config.h"
  39. #include "timer.h"
  40. #include "proc.h"
  41. #include "socket.h"
  42. #include "usock.h"
  43. #include "session.h"
  44. #include "smtp.h"
  45. #include "dirutil.h"
  46. #include "telnet.h"
  47. #include "ftp.h"
  48. #include "ftpserv.h"
  49. #include "commands.h"
  50. #include "netuser.h"
  51. #include "files.h"
  52. #include "bm.h"
  53. #include "mailbox.h"
  54. #include "ax25mail.h"
  55. #include "nr4mail.h"
  56. #include "cmdparse.h"
  57.  
  58. /*
  59. #define MBDEBUG
  60. */
  61.  
  62. struct mbx *Mbox[NUMMBX];
  63. static char *Motd = NULLCHAR;
  64. static int Attended = TRUE;    /* default to attended mode */
  65. unsigned Maxlet = BM_NLET;
  66.  
  67. char Noperm[] = "Permission denied.\n";
  68. char Nosock[] = "Can't create socket\n";
  69.  
  70. static char Mbbanner[] = "[NET-H$]\nWelcome %s to the %s TCP/IP Mailbox (%s)\n%s";
  71. static char Mbmenu[] = "Current msg# %d : A,B,C,D,E,F,G,H,I,J,K,L,N,R,S,T,U,V,W,Z,? >\n";
  72. static char Longmenu1[] = "(?)help    (A)rea     (B)ye      (C)hat     (D)ownload (E)scape   (F)inger\n";
  73. static char Longmenu2[] = "(G)ateway  (H)elp     (I)nfo     (J)heard   (K)ill     (L)ist     (N)etrom\n";
  74. static char Longmenu3[] = "(R)ead     (S)end     (T)elnet   (U)pload   (V)erbose  (W)hat     (Z)ap\n";
  75. static char Loginbanner[] = "\nKA9Q NOS (%s)\n\n";
  76. static char Howtoend[] = "Terminate with /EX or ^Z in first column (^A aborts):\n";
  77.  
  78. /* Prototypes for functions defined in mailbox.c */
  79. int dombox(int argc,
  80.            char **argv,
  81.            void *p);
  82. static int doattend(int argc,
  83.                     char **argv,
  84.                     void *p);
  85. static int domaxmsg(int argc,
  86.                     char **argv,
  87.                     void *p);
  88. static int domotd(int argc,
  89.                   char **argv,
  90.                   void *p);
  91. int domboxdisplay(int argc,
  92.                   char **argv,
  93.                   void *p);
  94. static int dotimeout(int argc,
  95.                      char **argv,
  96.                      void *p);
  97. void listusers(int s);
  98. struct mbx *newmbx(void);
  99. static int mbx_getname(struct mbx *m);
  100. void __stdargs mbx_incom(int s,
  101.                void *t,
  102.                void *p);
  103. void exitbbs(struct mbx *m);
  104. int mbx_parse(struct mbx *m);
  105. int mbxrecvline(int s,
  106.                 char *buf,
  107.                 int len,
  108.                 int escape);
  109. int domboxbye(int argc,
  110.               char **argv,
  111.               void *p);
  112. static int dombhelp(int argc,
  113.                     char **argv,
  114.                     void *p);
  115. static int dochat(int argc,
  116.                   char **argv,
  117.                   void *p);
  118. static int dosend(int argc,
  119.                   char **argv,
  120.                   void *p);
  121. static int dosid(int argc,
  122.                  char **argv,
  123.                  void *p);
  124. int dombescape(int argc,
  125.                char **argv,
  126.                void *p);
  127. static int dodownload(int argc,
  128.                       char **argv,
  129.                       void *p);
  130. static int dombupload(int argc,
  131.                       char **argv,
  132.                       void *p);
  133. static int dowhat(int argc,
  134.                   char **argv,
  135.                   void *p);
  136. static int dozap(int argc,
  137.                  char **argv,
  138.                  void *p);
  139. static int dosysop(int argc,
  140.                    char **argv,
  141.                    void *p);
  142. static int dostars(int argc,
  143.                    char **argv,
  144.                    void *p);
  145. static int doarea(int argc,
  146.                   char **argv,
  147.                   void *p);
  148. void changearea(struct mbx *m,
  149.                 char *area);
  150. static int dombtelnet(int argc,
  151.                       char **argv,
  152.                       void *p);
  153. static int dombfinger(int argc,
  154.                       char **argv,
  155.                       void *p);
  156. int gw_connect(struct mbx *m,
  157.                int s,
  158.                char *fsocket,
  159.                int len);
  160. static void __stdargs gw_input(int s,
  161.                      void *notused,
  162.                      void *p);
  163. static void __stdargs gw_superv(int null,
  164.                       void *proc,
  165.                       void *p);
  166. static void gw_alarm(void *p);
  167. static int mbx_to(int argc,
  168.                   char **argv,
  169.                   void *p);
  170. static int mbx_data(struct mbx *m,
  171.                     struct list *cclist,
  172.                     char *extra);
  173. static int msgidcheck(char *string);
  174. char *rewrite_address(char *addr);
  175. static int uuencode(FILE *infile,
  176.                     int s,
  177.                     char *infilename);
  178. static int check_mail_privilege(char *name);
  179. static void string_tidy(char *str);
  180.  
  181. /************************************************************************/
  182. /*            C O M M A N D S                    */
  183. /************************************************************************/
  184.  
  185. static int doattend __ARGS((int argc,char *argv[],void *p));
  186. static int domaxmsg __ARGS((int argc,char *argv[],void *p));
  187. static int domotd __ARGS((int argc,char *argv[],void *p));
  188. static int dotimeout __ARGS((int argc,char *argv[],void *p));
  189. static int check_mail_privilege __ARGS((char *name));
  190. static void string_tidy __ARGS((char *str));
  191.  
  192. /* mbox subcommand table */
  193. static struct cmds Mbtab[] = {
  194.     "attend",    doattend,    0, 0, NULLCHAR,
  195. #ifdef    AX25
  196.     "kick",        dombkick,    0, 0, NULLCHAR,
  197. #endif
  198.     "maxmsg",    domaxmsg,    0, 0, NULLCHAR,
  199.     "motd",        domotd,        0, 0, NULLCHAR,
  200.     "status",    domboxdisplay,    0, 0, NULLCHAR,
  201. #ifdef    AX25
  202.     "timer",    dombtimer,    0, 0, NULLCHAR,
  203. #endif
  204.     "tiptimeout",    dotimeout,    0, 0, NULLCHAR,
  205.     NULLCHAR,
  206. };
  207.  
  208.  
  209. int
  210. dombox(argc,argv,p)
  211. int argc;
  212. char *argv[];
  213. void *p;
  214. {
  215.     if(argc == 1)
  216.         return domboxdisplay(argc,argv,p);
  217.     return subcmd(Mbtab,argc,argv,p);
  218. }
  219.  
  220. /* if unattended mode is set, ax25, telnet and maybe other sessions will
  221.  * be restricted.
  222.  */
  223. static int
  224. doattend(argc,argv,p)
  225. int argc;
  226. char *argv[];
  227. void *p;
  228. {
  229.     return setbool(&Attended,"Attended flag",argc,argv);
  230. }
  231.  
  232. static int
  233. domaxmsg(argc,argv,p)
  234. int argc;
  235. char *argv[];
  236. void *p;
  237. {
  238.     return setuns(&Maxlet,"Maximum messages per area",argc,argv);
  239. }
  240.  
  241. static int
  242. domotd(argc,argv,p)
  243. int argc;
  244. char *argv[];
  245. void *p;
  246. {
  247.     if(argc > 2) {
  248.         tprintf("Usage: mbox motd \"<your message>\"\n");
  249.         return 0;
  250.     }
  251.  
  252.     if(argc < 2) {
  253.         if(Motd != NULLCHAR)
  254.             tputs(Motd);
  255.     }
  256.     else {
  257.         if(Motd != NULLCHAR){
  258.             free(Motd);
  259.             Motd = NULLCHAR;    /* reset the pointer */
  260.         }
  261.         if(!strlen(argv[1]))
  262.             return 0;        /* clearing the buffer */
  263.         Motd = mallocw(strlen(argv[1])+5);/* allow for the EOL char */
  264.         strcpy(Motd, argv[1]);
  265.         strcat(Motd, "\n");        /* add the EOL char */
  266.     }
  267.     return 0;
  268. }
  269.  
  270. int
  271. domboxdisplay(argc,argv,p)
  272. int argc;
  273. char *argv[];
  274. void *p;
  275. {
  276.     int i, j, len;
  277.     struct mbx *m;
  278.     char fsocket[MAXSOCKSIZE];
  279.     static char *states[] = {"LOGIN","CMD","SUBJ","DATA","REVFWD",
  280.                 "TRYING","FORWARD"};
  281.  
  282.     tprintf("User       State    S#  Where\n");
  283.  
  284.     for (i = 0; i < NUMMBX; i++){
  285.         if((m = Mbox[i]) != NULLMBX){
  286.             len = MAXSOCKSIZE;
  287.             j = getpeername(m->user,fsocket,&len);
  288.             if(tprintf("%-11s%-9s%-4u%s\n",m->name,
  289.                    states[m->state],m->user,
  290.                    j != -1 ? psocket(fsocket): "") == EOF)
  291.                 break;
  292.         }
  293.     }
  294.     return 0;
  295. }
  296.  
  297. static int
  298. dotimeout(argc,argv,p)
  299. int argc;
  300. char *argv[];
  301. void *p;
  302. {
  303.     return setuns(&Tiptimeout,"Tip connection timeout",argc,argv);
  304. }
  305.  
  306.  
  307. /**********************************************************************/
  308.  
  309. void
  310. listusers(s)
  311. int s;
  312. {
  313.     int outsave;
  314.  
  315.     usprintf(s,"\nCurrent remote users:\n");
  316.     outsave = Curproc->output;
  317.     Curproc->output = s;
  318.     domboxdisplay(0,NULLCHARP,NULL);
  319.     Curproc->output = outsave;
  320. }
  321.  
  322. struct mbx *
  323. newmbx()
  324. {
  325.     int i;
  326.     struct mbx *m;
  327.  
  328.     for(i = 0; i < NUMMBX; i++){
  329.         if(Mbox[i] == NULLMBX){
  330.             m = Mbox[i] = (struct mbx *)callocw(1,sizeof(struct mbx));
  331.             m->mbnum = i;
  332.             return m;
  333.         }
  334.     }
  335.     /* If we get here, there are no free mailbox sessions */
  336.     return NULLMBX;
  337. }
  338.  
  339. static int
  340. mbx_getname(m)
  341. struct mbx *m;
  342. {
  343. #ifdef    AX25
  344.     char *cp;
  345. #endif
  346.     union sp sp;
  347.     char tmp[MAXSOCKSIZE];
  348.     char buf[MBXLINE];
  349.     char *buf1;
  350.     char *password;
  351.     int len = MAXSOCKSIZE;
  352.     int anony = 0;
  353.     int oldmode;
  354.  
  355.     sp.p = tmp;
  356.     sp.sa->sa_family = AF_LOCAL;    /* default to AF_LOCAL */
  357.     getpeername(m->user,tmp,&len);
  358.     m->path = mallocw(MBXLINE);
  359.     /* This is one of the two parts of the mbox code that depends on the
  360.      * underlying protocol. We have to figure out the name of the
  361.      * calling station. This is only practical when AX.25 or NET/ROM is
  362.      * used. Telnet users have to identify themselves by a login procedure.
  363.      */
  364.     switch(sp.sa->sa_family){
  365. #ifdef    AX25
  366.     case AF_NETROM:
  367.     case AF_AX25:
  368.         /* NETROM and AX25 socket address structures are "compatible" */
  369.         pax25(m->name,sp.ax->ax25_addr);
  370.         cp = strchr(m->name,'-');
  371.         if(cp != NULLCHAR)            /* get rid of SSID */
  372.             *cp = '\0';
  373.         /* SMTP wants the name to be in lower case */
  374.         cp = m->name;
  375.         while(*cp){
  376.             if(isupper(*cp))
  377.                 *cp = tolower(*cp);
  378.             ++cp;
  379.         }
  380.         anony = 1;
  381.         /* Try to find the privileges of this user from the userfile */
  382.         if((m->privs = userlogin(m->name,buf,&m->path,MBXLINE,&anony)) == -1)
  383.             if((m->privs = userlogin("bbs",buf,&m->path,MBXLINE,&anony)) == -1)
  384.                 if((m->privs = userlogin("anonymous",buf,&m->path,MBXLINE,
  385.                      &anony)) == -1){
  386.                         m->privs = 0;
  387.                         free(m->path);
  388.                         m->path = NULLCHAR;
  389.                 }
  390.         if(m->privs & EXCLUDED_CMD)
  391.             return -1;
  392.         return 0;
  393. #endif
  394.     case AF_LOCAL:
  395.     case AF_INET:
  396.         m->state = MBX_LOGIN;
  397.         tprintf(Loginbanner,Hostname);
  398.         for(;;){
  399.             tputs("login: ");
  400.             usflush(m->user);
  401.             if(mbxrecvline(m->user,m->name,sizeof(m->name),-1) == EOF)
  402.                 return -1;
  403.             if(*m->name == '\0')
  404.                 continue;
  405.  
  406.             if ((buf1=userlookup(m->name,&password,NULLCHARP,NULLINT,NULL))!=NULLCHAR)
  407.             {
  408.                 if (strcmp(password,"@")==0)
  409.                     tprintf("*** Enter your email address as your password\n");
  410.                 free(buf1);
  411.             }
  412.  
  413.             tprintf("Password: %c%c%c",IAC,WILL,TN_ECHO);
  414.             usflush(m->user);
  415.             oldmode = sockmode(m->user,SOCK_BINARY);
  416.             if(mbxrecvline(m->user,buf,MBXLINE,-1) == EOF)
  417.                 return -1;
  418.             tprintf("%c%c%c",IAC,WONT,TN_ECHO);
  419.             sockmode(m->user,oldmode);
  420.             tputc('\n');
  421.             usflush(m->user);
  422.             /* This is needed if the password was send before the
  423.              * telnet no-echo options were receied. We neeed to
  424.              * flush the eold sequence from the input buffers, sigh
  425.              */
  426.             if(socklen(m->user,0))/* discard any remaining input */
  427.                 recv_mbuf(m->user,NULL,0,NULLCHAR,0);
  428.             if((m->privs = userlogin(m->name,buf,&m->path,MBXLINE,&anony))
  429.              != -1){
  430.                 if(anony)
  431.                     log(m->user,"MBOX login: %s Password: %s",m->name,buf);
  432.                 else
  433.                     log(m->user,"MBOX login: %s",m->name);
  434.                 if(m->privs & EXCLUDED_CMD)
  435.                     return -1;
  436.                 return 0;
  437.             }
  438.             tprintf("Login incorrect\n");
  439.             *m->name = '\0';    /* wipe any garbage */
  440.         }
  441.     }
  442.     return 0;
  443. }
  444.  
  445. /* Incoming mailbox session */
  446. void __stdargs
  447. mbx_incom(s,t,p)
  448. int s;
  449. void *t;
  450. void *p;
  451. {
  452.     struct mbx *m;
  453.     struct usock *up;
  454.     char *buf[3];
  455.     int rval;
  456.  
  457.     sockmode(s,SOCK_ASCII);
  458.     sockowner(s,Curproc);    /* We own it now */
  459.     /* Secede from the parent's sockets, and use the network socket that
  460.      * was passed to us for both input and output. The reference
  461.      * count on this socket will still be 1; this allows the domboxbye()
  462.      * command to work by closing that socket with a single call.
  463.      * If we return, the socket will be closed automatically.
  464.      */
  465.     close_s(Curproc->output);
  466.     close_s(Curproc->input);
  467.     Curproc->output = Curproc->input = s;
  468.  
  469.     /* We'll do our own flushing right before we read input */
  470.     setflush(s,-1);
  471.  
  472.     log(s,"open MBOX");
  473.     if((m = newmbx()) == NULLMBX){
  474.         tprintf("Too many mailbox sessions\n");
  475.         return;
  476.     }
  477.     m->user = s;
  478.     m->escape = 24;        /* default escape character is Ctrl-X */
  479.     m->type = (int) t;
  480.     /* get the name of the remote station */
  481.     if(mbx_getname(m) == -1) {
  482.         exitbbs(m);
  483.         return;
  484.     }
  485.  
  486.     m->state = MBX_CMD;    /* start in command state */
  487.  
  488.     /* Now say hi */
  489.     tprintf(Mbbanner,m->name,Hostname,Version,
  490.         Motd != NULLCHAR ? Motd : "");
  491.     /* Enable our local message area */
  492.     buf[1] = m->name;
  493.     doarea(2,buf,m);
  494.     tprintf(Mbmenu,m->current);
  495.     while(mbxrecvline(s,m->line,MBXLINE,-1) != EOF){
  496.         if((rval = mbx_parse(m)) == -2)
  497.             break;
  498.         if(rval == 1)
  499.             tprintf("Bad syntax.\n");
  500.         if(!(m->sid & MBX_SID) && isnewprivmail(m) > 0L)
  501.             tprintf("You have new mail.\n");
  502.         scanmail(m);
  503.         tprintf((m->sid & MBX_SID) ? ">\n" : Mbmenu, m->current);
  504.         m->state = MBX_CMD;
  505.     }
  506.     exitbbs(m);
  507.     /* nasty hack! we may have screwed up reference count */
  508.     /* by invoking newproc("smtp_send",....); Fudge it!   */
  509.     if((up = itop(Curproc->output)) != NULLUSOCK)
  510.         up->refcnt = 1;
  511.     close_s(Curproc->output);
  512. }
  513.  
  514. void
  515. exitbbs(m)
  516. struct mbx *m;
  517. {
  518.     closenotes(m);
  519.     free(m->to);
  520.     free(m->tofrom);
  521.     free(m->origto);
  522.     free(m->tomsgid);
  523.     free(m->path);
  524.     free((char *)m->mbox);
  525.     Mbox[m->mbnum] = NULLMBX;
  526.     free((char *)m);
  527. }
  528.  
  529. /**********************************************************************/
  530.  
  531. static int dochat __ARGS((int argc,char *argv[],void *p));
  532. static int dodownload __ARGS((int argc,char *argv[],void *p));
  533. static int dombupload __ARGS((int argc,char *argv[],void *p));
  534. static int dowhat __ARGS((int argc,char *argv[],void *p));
  535. static int dozap __ARGS((int argc,char *argv[],void *p));
  536. static int dosend __ARGS((int argc,char *argv[],void *p));
  537. static int dosid __ARGS((int argc,char *argv[],void *p));
  538. static int dosysop __ARGS((int argc,char *argv[],void *p));
  539. static int dostars __ARGS((int argc,char *argv[],void *p));
  540. static int dombhelp __ARGS((int argc,char *argv[],void *p));
  541. static int dombtelnet __ARGS((int argc,char *argv[],void *p));
  542. static int dombfinger __ARGS((int argc,char *argv[],void *p));
  543. static void gw_alarm __ARGS((void *p));
  544. static void __stdargs gw_input __ARGS((int s,void *notused,void *p));
  545. static void __stdargs gw_superv __ARGS((int null,void *proc,void *p));
  546. static int mbx_to __ARGS((int argc,char *argv[],void *p));
  547. static int mbx_data __ARGS((struct mbx *m,struct list *cclist,char *extra));
  548. static int msgidcheck __ARGS((char *string));
  549. static int uuencode __ARGS((FILE *infile,int s,char *infilename));
  550.  
  551. static struct cmds Mbcmds[] = {
  552.     "",        doreadnext,    0, 0, NULLCHAR,
  553.     "area",        doarea,        0, 0, NULLCHAR,
  554.     "send",        dosend,        0, 0, NULLCHAR,
  555.     "read",        doreadmsg,    0, 2, "R numbers",
  556.     "verbose",    doreadmsg,    0, 2, "V numbers",
  557. #ifdef    AX25
  558.     "jheard",    doaxheard,    0, 0, NULLCHAR,
  559. #endif
  560.     "kill",        dodelmsg,    0, 2, "K numbers",
  561.     "list",        dolistnotes,    0, 0, NULLCHAR,
  562.     "escape",    dombescape,    0, 0, NULLCHAR,
  563.     "download",    dodownload,    0, 2, "D[U] filename",
  564.     "upload",    dombupload,    0, 2, "U filename",
  565.     "what",        dowhat,        0, 0, NULLCHAR,
  566.     "zap",        dozap,        0, 2, "Z filename",
  567. #ifdef AX25
  568.     "gateway",    dogateway,    0, 3, "G interface callsigns",
  569. #endif
  570.     "telnet",    dombtelnet,    0, 2, "T hostname",
  571.     "finger",    dombfinger,    0, 0, NULLCHAR,
  572. #ifdef    NETROM
  573.     "netrom",    dombnetrom,    0, 0, NULLCHAR,
  574. #endif
  575.     "chat",        dochat,        0, 0, NULLCHAR,
  576.     "bye",        domboxbye,    0, 0, NULLCHAR,
  577.     "help",        dombhelp,    0, 0, NULLCHAR,
  578.     "info",        dombhelp,    0, 0, NULLCHAR,
  579.     "?",        dombhelp,    0, 0, NULLCHAR,
  580.     "[",        dosid,        0, 0, NULLCHAR,
  581. #ifdef    AX25
  582.     "f>",        dorevfwd,    0, 0, NULLCHAR,
  583. #endif
  584.     "@",        dosysop,    0, 0, NULLCHAR,
  585.     "***",        dostars,    0, 0, NULLCHAR,
  586.     NULLCHAR,    NULLFP,        0, 0, "Huh?",
  587. };
  588.  
  589. /* "twocmds" defines the MBL/RLI two-letter commands, eg. "SB", "SP" and so on.
  590.  * They have to be treated specially since cmdparse() wants a space between
  591.  * the actual command and its arguments.
  592.  * "SP FOO" is converted to "s  foo" and the second command letter is saved
  593.  * in m->stype. Longer commands like "SEND" are unaffected, except for
  594.  * commands starting with "[", i.e. the SID, since we don't know what it will
  595.  * look like.
  596.  */
  597. static char twocmds[] = "slrd[";    /* S,L,R,D are two-letter commands */
  598. int
  599. mbx_parse(m)
  600. struct mbx *m;
  601. {
  602.     char *cp;
  603.     int i;
  604.     char *newargv[2];
  605.     /* Translate entire buffer to lower case */
  606.     for (cp = m->line; *cp != '\0'; ++cp)
  607.         if(isupper(*cp))
  608.             *cp = tolower(*cp);
  609.     /* Skip any spaces at the begining */
  610.     for(cp = m->line;isspace(*cp);++cp)
  611.         ;
  612.     m->stype = ' ';
  613.     if(*cp != '\0' && *(cp+1) != '\0')
  614.     for(i=0; i<strlen(twocmds); ++i){
  615.         if(*cp == twocmds[i] && (isspace(*(cp+2)) || *(cp+2) == '\0'
  616.          || *cp == '[')){
  617.             if(islower(*(++cp)))
  618.                 m->stype = toupper(*cp); /* Save the second character */
  619.             else
  620.                 m->stype = *cp;
  621.             *cp = ' ';
  622.             break;
  623.         }
  624.     }
  625.     /* See if the input line consists solely of digits */
  626.     cp = m->line;
  627.     for(cp = m->line;isspace(*cp);++cp)
  628.         ;
  629.     newargv[1] = cp;
  630.     for(;*cp != '\0' && isdigit(*cp);++cp)
  631.         ;
  632.     if(*cp == '\0' && strlen(newargv[1]) > 0) {
  633.         newargv[0] = "read";
  634.         return doreadmsg(2,newargv,(void *)m);
  635.     }
  636.     else
  637.         return cmdparse(Mbcmds,m->line,(void *)m);
  638. }
  639.  
  640. /* This works like recvline(), but telnet options are answered and the
  641.  * terminating newline character is not put into the buffer. If the
  642.  * incoming character equals the value of escape, any queued input is
  643.  * flushed and -2 returned.
  644.  */
  645. int
  646. mbxrecvline(s,buf,len,escape)
  647. int s;
  648. char *buf;
  649. int len;
  650. int escape;
  651. {
  652.     int c, cnt = 0;
  653. #ifdef    foo
  654.     int opt
  655. #endif
  656.  
  657.     if(buf == NULLCHAR)
  658.         return 0;
  659.     usflush(Curproc->output);
  660.     while((c = recvchar(s)) != EOF){
  661.         if(c == IAC){        /* Telnet command escape */
  662.             if((c = recvchar(s)) == EOF)
  663.                 break;
  664. #ifndef    foo
  665.             if(c > 250 && c < 255 && recvchar(s) != EOF){
  666. #endif
  667. #ifdef    foo
  668.             if(c > 250 && c < 255 && (opt = recvchar(s)) != EOF){
  669.                 switch(c){
  670.                 case WILL:
  671.                     tprintf("%c%c%c",IAC,DONT,opt);
  672.                     break;
  673.                 case WONT:
  674.                     tprintf("%c%c%c",IAC,DONT,opt);
  675.                     break;
  676.                 case DO:
  677.                     tprintf("%c%c%c",IAC,WONT,opt);
  678.                     break;
  679.                 case DONT:
  680.                     tprintf("%c%c%c",IAC,WONT,opt);
  681.                 }
  682. #endif
  683. /* to be fixed             usflush(Curproc->output);*/
  684.                 continue;
  685.             }
  686.             if(c != IAC && (c = recvchar(s)) == EOF)
  687.                 break;
  688.         }
  689.         /* ordinary character */
  690.         if(c == '\r' || c == '\n')
  691.             break;
  692.         if(uchar(c) == escape){
  693.             if(socklen(s,0)) /* discard any remaining input */
  694.                 recv_mbuf(s,NULL,0,NULLCHAR,0);
  695.             cnt = -2;
  696.             break;
  697.         }
  698.         *buf++ = c;
  699.         ++cnt;
  700.         if(cnt == len - 1)
  701.             break;
  702.     }
  703.     if(c == EOF && cnt == 0)
  704.         return -1;
  705.     *buf = '\0';
  706.     return cnt;
  707. }
  708.  
  709. int
  710. domboxbye(argc,argv,p)
  711. int argc;
  712. char *argv[];
  713. void *p;
  714. {
  715.     struct mbx *m;
  716.  
  717.     m = (struct mbx *)p;
  718.     /* Now say goodbye */
  719.     tprintf("Thank you %s, for calling the %s Tcp/Ip Mailbox.\n",m->name,
  720.         Hostname);
  721.     if(m->type == TIP)
  722.         tprintf("Please hang up now.\n");
  723.  
  724.     return -2;    /* signal that exitbbs() should be called */
  725. }
  726. static int
  727. dombhelp(argc,argv,p)
  728. int argc;
  729. char *argv[];
  730. void *p;
  731. {
  732.     char buf[255];
  733.     int i;
  734.     FILE *fp;
  735.     if(*argv[0] == '?') {
  736.         tputs(Longmenu1);
  737.         tputs(Longmenu2);
  738.         tputs(Longmenu3);
  739.         return 0;
  740.     }
  741.     buf[0] = '\0';
  742.     if(argc > 1)
  743.         for(i=0; Mbcmds[i].name != NULLCHAR; ++i)
  744.             if(!strncmp(Mbcmds[i].name,argv[1],strlen(argv[1]))) {
  745.                 sprintf(buf,"%s/%s.hlp",Helpdir,Mbcmds[i].name);
  746.                 break;
  747.             }
  748.     if(buf[0] == '\0')
  749.         if(*argv[0] == 'i')            /* INFO command */
  750.             sprintf(buf,"%s/info.hlp",Helpdir);
  751.         else
  752.             sprintf(buf,"%s/help.hlp",Helpdir);
  753.     if((fp = fopen(buf,READ_TEXT)) != NULLFILE) {
  754.         sendfile(fp,Curproc->output,ASCII_TYPE,0,0);
  755.         fclose(fp);
  756.     }
  757.     else
  758.         tprintf("No help available. (%s not found)\n",buf);
  759.     return 0;
  760. }
  761.  
  762. static int
  763. dochat(argc,argv,p)
  764. int argc;
  765. char *argv[];
  766. void *p;
  767. {
  768.     char buf[8], *newargv[3];
  769.  
  770.     if(Attended){
  771.         newargv[0] = "telnet";
  772.         newargv[1] = Hostname;
  773.         sprintf(buf,"%d",IPPORT_TTYLINK);
  774.         newargv[2] = buf;
  775.         return dombtelnet(3,newargv,p);
  776.     }
  777.     else {
  778.         tprintf("Sorry - the system is unattended.\007\n");
  779.     }
  780.     /* It returns only after a disconnect or refusal */
  781.     return 0;
  782. }
  783.  
  784. static int
  785. dosend(argc,argv,p)
  786. int argc;
  787. char *argv[];
  788. void *p;
  789. {
  790.     int cccnt = 0, fail = 0;
  791.     char *host, *cp, fullfrom[MBXLINE], sigwork[LINELEN], *rhdr = NULLCHAR;
  792.     struct list *ap, *cclist = NULLLIST;
  793.     struct mbx *m;
  794.     FILE *fp;
  795.  
  796.     m = (struct mbx *)p;
  797.     if((m->stype != 'R' || (m->sid & MBX_SID)) && mbx_to(argc,argv,m)
  798.        == -1){
  799.         if(m->sid & MBX_SID)
  800.             tprintf("NO - syntax error\n");
  801.         else {
  802.             tprintf("S command syntax error - format is:\n");
  803.             tprintf("  S[F] name [@ host] [< from_addr] [$bulletin_id]\n");
  804.             tprintf("  SR [number]\n");
  805.         }
  806.         return 0;
  807.     }
  808.     if(m->stype != 'R' && msgidcheck(m->tomsgid)) {
  809.         if(m->sid & MBX_SID)
  810.             tputs("NO - ");
  811.         tprintf("Already have %s\n",m->tomsgid);
  812.         return 0;
  813.     }
  814.     if(m->stype == 'R' && !(m->sid & MBX_SID) &&
  815.        mbx_reply(argc,argv,m,&cclist,&rhdr) == -1)
  816.         return 0;
  817.     if((cp = rewrite_address(m->to)) != NULLCHAR)
  818.          if(strcmp(m->to,cp) != 0){
  819.           m->origto = m->to;
  820.           m->to = cp;
  821.          }
  822.          else
  823.           free(cp);
  824.     if((m->origto != NULLCHAR || m->stype == 'R') && !(m->sid & MBX_SID))
  825.         tprintf("To: %s\n", m->to);
  826.     if(validate_address(m->to) == 0){
  827.         if(m->sid & MBX_SID)
  828.             tprintf("NO - bad address\n");
  829.         else
  830.             tprintf("Bad user or host name\n");
  831.         free(rhdr);
  832.         del_list(cclist);
  833.         /* We don't free any more buffers here. They are freed upon
  834.          * the next call to mbx_to() or to domboxbye()
  835.          */
  836.         return 0;
  837.     }
  838.  
  839. /* Check to ensure that users without Telnet access don't use external
  840.    Email - added by paul@wolf.demon.co.uk */
  841.     if (!(m->privs & TELNET_CMD)){
  842. /* Bang paths (!), redirectors (%) and hosts other than Hostname are
  843.    disallowed */
  844.         fail+=check_mail_privilege(m->to);
  845.  
  846.         for ( ap = cclist; ap != NULLLIST; ap = ap->next) {
  847.             fail+=check_mail_privilege(ap->val);
  848.         }
  849.  
  850.         if (fail){
  851.             tprintf(Noperm);
  852.             free(rhdr);
  853.             del_list(cclist);
  854.             /* We don't free any more buffers here. They are freed upon
  855.              * the next call to mbx_to() or to domboxbye()
  856.              */
  857.             return 0;
  858.         }
  859.  
  860.  
  861.     }
  862.  
  863.     /* Display the Cc: line (during SR command) */
  864.     for(ap = cclist; ap != NULLLIST; ap = ap->next) {
  865.         if(cccnt == 0){
  866.             tprintf("%s",Hdrs[CC]);
  867.             cccnt = 4;
  868.         }
  869.         else {
  870.             tputs(", ");
  871.             cccnt += 2;
  872.         }
  873.         if(cccnt + strlen(ap->val) > 80 - 3) {
  874.             tputs("\n    ");
  875.             cccnt = 4;
  876.         }
  877.         tputs(ap->val);
  878.         cccnt += strlen(ap->val);
  879.     }
  880.     if(cccnt)
  881.         tputc('\n');
  882.     m->state = MBX_SUBJ;
  883.     if(m->stype != 'R' || (m->sid & MBX_SID) != 0) {
  884.         tprintf((m->sid & MBX_SID) ? "OK\n" : "Subject: ");
  885.         if(mbxrecvline(m->user,m->line,MBXLINE,-1) == -1)
  886.             return 0;
  887.     }
  888.     else                /* Replying to a message */
  889.         tprintf("Subject: %s\n",m->line);
  890.     if(mbx_data(m,cclist,rhdr) == -1){
  891.         free(rhdr);
  892.         del_list(cclist);
  893.         tputs("Can't create temp file for mail\n");
  894.         return 0;
  895.     }
  896.     free(rhdr);
  897.     m->state = MBX_DATA;
  898.     if((m->sid & MBX_SID) == 0 && m->stype != 'F')
  899.         tprintf("Enter message.  %s",Howtoend);
  900.  
  901.     if(m->stype != 'F' || (m->sid & MBX_SID) != 0)
  902.         while(mbxrecvline(m->user,m->line,MBXLINE,-1) != -1){
  903.             if(m->line[0] == 0x01){  /* CTRL-A */
  904.                 fclose(m->tfile);
  905.                 tputs("Aborted.\n");
  906.                 del_list(cclist);
  907.                 return 0;
  908.             }
  909.             if(m->line[0] != CTLZ && stricmp(m->line, "/ex"))
  910.                 fprintf(m->tfile,"%s\n",m->line);
  911.             else
  912.                 break;    /* all done */
  913.         }
  914.     else {
  915.         fprintf(m->tfile,"----- Forwarded message -----\n\n");
  916.         msgtofile(m,m->current,m->tfile,0);
  917.         fprintf(m->tfile,"----- End of forwarded message -----\n");
  918.     }
  919.  
  920.     /* Insert customised signature if one is found */
  921.     if(!(m->sid & MBX_SID)) {    /* not a forwarding BBS */
  922.          sprintf(sigwork,"%s/%s.sig",Signature,
  923.              m->tofrom ? m->tofrom : m->name);
  924.          if((fp = fopen(sigwork,READ_TEXT)) != NULLFILE){
  925.           while(fgets(sigwork,LINELEN,fp) != NULLCHAR)
  926.             fputs(sigwork,m->tfile);
  927.           fclose(fp);
  928.          }
  929.     }
  930.  
  931.     if((host = strrchr(m->to,'@')) == NULLCHAR) {
  932.         host = Hostname;    /* use our hostname */
  933.         if(m->origto != NULLCHAR) {
  934.             /* rewrite_address() will be called again by our
  935.              * SMTP server, so revert to the original address.
  936.              */
  937.              free(m->to);
  938.             m->to = m->origto;
  939.             m->origto = NULLCHAR;
  940.         }
  941.     }
  942.     else
  943.         host++;    /* use the host part of address */
  944.  
  945.     /* make up full from name for work file */
  946.     if(m->tofrom != NULLCHAR)
  947.         sprintf(fullfrom,"%s%%%s.bbs@%s",m->tofrom, m->name, Hostname);
  948.     else
  949.         sprintf(fullfrom,"%s@%s",m->name,Hostname);
  950.     if(cclist != NULLLIST && stricmp(host,Hostname) != 0) {
  951.         fseek(m->tfile,0L,0);    /* reset to beginning */
  952.         fail = queuejob(m->tfile,Hostname,cclist,fullfrom,NULL);
  953.         del_list(cclist);
  954.         cclist = NULLLIST;
  955.     }
  956.     addlist(&cclist,m->to,0);
  957.     fseek(m->tfile,0L,0);
  958.     fail += queuejob(m->tfile,host,cclist,fullfrom,NULL);
  959.     del_list(cclist);
  960.     fclose(m->tfile);
  961.     if(fail)
  962.          tputs("Couldn't queue message for delivery\n");
  963.     else
  964.          if(m->tomsgid != NULLCHAR &&
  965.         (fp = fopen(Historyfile,APPEND_TEXT)) != NULLFILE) {
  966.           fprintf(fp,"%s\n",m->tomsgid); /* Save BID in history file */
  967.           fclose(fp);
  968.          }
  969.     smtptick(NULL);        /* wake SMTP to send that mail */
  970.     return 0;
  971. }
  972.  
  973. static int
  974. dosid(argc,argv,p)
  975. int argc;
  976. char *argv[];
  977. void *p;
  978. {
  979.     struct mbx *m;
  980.     char *cp;
  981.  
  982.     m = (struct mbx *)p;
  983.     if(argc == 1)
  984.         return 1;
  985.     if(argv[1][strlen(argv[1]) - 1] != ']') /* must be an SID */
  986.         return 1;
  987.     m->sid = MBX_SID;
  988.     /* Now check to see if this is an RLI board.
  989.      * As usual, Hank does it a bit differently from
  990.      * the rest of the world.
  991.      */
  992.     if(m->stype == 'R' && strncmp(argv[1],"li",2) == 0)/* [RLI] at a minimum */
  993.         m->sid |= MBX_RLI_SID;
  994.     /* Check to see if the BBS supports a kludge called "hierarchical
  995.      * routing designators."
  996.      *
  997.      * No need to check for ']' -- it must be there or this is not
  998.      * a valid mbox id -- it is checked earlier (fix de OH3LKU)
  999.      */
  1000.     if((cp = strchr(argv[1],'-')) != NULLCHAR
  1001.      && (cp=strchr(cp+1,'h')) != NULLCHAR
  1002.      && strchr(cp+1,'$'))
  1003.         m->sid |= MBX_HIER_SID;    
  1004.     return 0;
  1005. }
  1006.  
  1007. int
  1008. dombescape(argc,argv,p)
  1009. int argc;
  1010. char *argv[];
  1011. void *p;
  1012. {
  1013.     struct mbx *m;
  1014.  
  1015.     m = (struct mbx *)p;
  1016.     if(argc < 2){
  1017.         tprintf("The escape character is: ");
  1018.         if(m->escape < 32)
  1019.             tprintf("CTRL-%c\n",m->escape+'A'-1);
  1020.         else
  1021.             tprintf("'%c'\n",m->escape);
  1022.         return 0;
  1023.     }
  1024.     if(strlen(argv[1]) > 1)
  1025.         if(isdigit(*argv[1]))
  1026.             m->escape = (char) atoi(argv[1]);
  1027.         else
  1028.             return 1;
  1029.     else
  1030.         m->escape = *argv[1];
  1031.     return 0;
  1032. }
  1033.  
  1034. static int
  1035. dodownload(argc,argv,p)
  1036. int argc;
  1037. char *argv[];
  1038. void *p;
  1039. {
  1040.     struct mbx *m;
  1041.     FILE *fp;
  1042.     char *file;
  1043.  
  1044.     m = (struct mbx *)p;
  1045.     file = pathname(m->path,argv[1]);
  1046.     if(!permcheck(m->path,m->privs,RETR_CMD,file)){
  1047.         tprintf(Noperm);
  1048.         return 0;
  1049.     }
  1050.     if((fp = fopen(file,READ_TEXT)) == NULLFILE)
  1051.         tprintf("Can't open \"%s\": %s\n",file,strerror(errno));
  1052.     else
  1053.         if(m->stype == 'U'){            /* uuencode ? */
  1054.             fclose(fp);
  1055.             fp = fopen(file,READ_BINARY);    /* assume non-ascii */
  1056.             uuencode(fp,m->user,file);
  1057.         } else
  1058.             sendfile(fp,m->user,ASCII_TYPE,0,0);
  1059.     free(file);
  1060.     fclose(fp);
  1061.     return 0;
  1062. }
  1063.  
  1064. static int
  1065. dombupload(argc,argv,p)
  1066. int argc;
  1067. char *argv[];
  1068. void *p;
  1069. {
  1070.     struct mbx *m;
  1071.     FILE *fp;
  1072.     char *file, buf[LINELEN];
  1073.  
  1074.     m = (struct mbx *)p;
  1075.     file = pathname(m->path,argv[1]);
  1076.     if(!permcheck(m->path,m->privs,STOR_CMD,file)){
  1077.         tprintf(Noperm);
  1078.         return 0;
  1079.     }
  1080.     if((fp = fopen(file,WRITE_TEXT)) == NULLFILE){
  1081.         tprintf("Can't create \"%s\": %s\n",file,strerror(errno));
  1082.         free(file);
  1083.         return 0;
  1084.     }
  1085.     log(m->user,"MBOX upload: %s",file);
  1086.     tprintf("Send file,  %s",Howtoend);
  1087.     for(;;){
  1088.         if(mbxrecvline(m->user,buf,LINELEN,-1) == -1){
  1089.             unlink(file);
  1090.             break;
  1091.         }
  1092.         if(buf[0] == 0x01){  /* CTRL-A */
  1093.             unlink(file);
  1094.             tprintf("Aborted.\n");
  1095.             break;
  1096.         }
  1097.         if(buf[0] == CTLZ || !stricmp("/ex",buf))
  1098.             break;
  1099.         fputs(buf,fp);
  1100. #if !defined(UNIX) && !defined(__TURBOC__) && !defined(AMIGA)
  1101.         /* Needed only if the OS uses a CR/LF
  1102.          * convention and putc doesn't do
  1103.          * an automatic translation
  1104.          */
  1105.         if(putc('\r',fp) == EOF)
  1106.             break;
  1107. #endif
  1108.         if(putc('\n',fp) == EOF)
  1109.             break;
  1110.     }
  1111.     free(file);
  1112.     fclose(fp);
  1113.     return 0;
  1114. }
  1115.  
  1116. static int
  1117. dowhat(argc,argv,p)
  1118. int argc;
  1119. char *argv[];
  1120. void *p;
  1121. {
  1122.     struct mbx *m;
  1123.     FILE *fp;
  1124.     char *file;
  1125.  
  1126.     m = (struct mbx *)p;
  1127.     if(argc < 2)
  1128.         file = strdup(m->path);
  1129.     else
  1130.         file = pathname(m->path,argv[1]);
  1131.     if(!permcheck(m->path,m->privs,RETR_CMD,file)){
  1132.         tprintf(Noperm);
  1133.         return 0;
  1134.     }
  1135.     if((fp = dir(file,1)) == NULLFILE)
  1136.         tprintf("Can't read directory: \"%s\": %s\n",file,strerror(errno));
  1137.     else
  1138.         sendfile(fp,m->user,ASCII_TYPE,0,0);
  1139.     free(file);
  1140.     fclose(fp);
  1141.     return 0;
  1142. }
  1143.  
  1144. static int
  1145. dozap(argc,argv,p)
  1146. int argc;
  1147. char *argv[];
  1148. void *p;
  1149. {
  1150.     struct mbx *m;
  1151.     char *file;
  1152.  
  1153.     m = (struct mbx *)p;
  1154.     file = pathname(m->path,argv[1]);
  1155.     if(!permcheck(m->path,m->privs,DELE_CMD,file)){
  1156.         tprintf(Noperm);
  1157.         return 0;
  1158.     }
  1159.     if(unlink(file))
  1160.         tprintf("Zap failed: %s\n",strerror(errno));
  1161.     log(m->user,"MBOX Zap: %s",file);
  1162.     free(file);
  1163.     return 0;
  1164. }
  1165.  
  1166. static int
  1167. dosysop(argc,argv,p)
  1168. int argc;
  1169. char *argv[];
  1170. void *p;
  1171. {
  1172.     struct mbx *m;
  1173.     int c;
  1174.     extern struct cmds far Cmds[];
  1175.  
  1176.     m = (struct mbx *) p;
  1177.     if(!(m->privs & SYSOP_CMD)){
  1178.         tprintf(Noperm);
  1179.         return 0;
  1180.     }
  1181.     dombescape(1,NULLCHARP,p);
  1182.     for(;;){
  1183.         tprintf("Net> ");
  1184.         usflush(Curproc->output);
  1185.         c = mbxrecvline(Curproc->input,m->line,MBXLINE,m->escape);
  1186.         if(c == EOF || c == -2)
  1187.             break;
  1188.         log(m->user,"MBOX sysop: %s",m->line);
  1189.         cmdparse(Cmds,m->line,NULL);
  1190.     }
  1191.     return 0;
  1192. }
  1193.  
  1194. /* Handle the "*** Done" command when reverse forwarding ends or the
  1195.  * "*** LINKED to" command.
  1196.  */
  1197. static int
  1198. dostars(argc,argv,p)
  1199. int argc;
  1200. char *argv[];
  1201. void *p;
  1202. {
  1203.     struct mbx *m;
  1204.     int anony = 1;
  1205.     m = (struct mbx *)p;
  1206.     /* The "*** LINKED to" command is only allowed to stations with
  1207.      * SYSOP privileges to prevent others from obtaining the same.
  1208.      */
  1209.     if((m->privs & SYSOP_CMD) && argc == 4 && !strcmp(argv[1],"linked")) {
  1210.         strcpy(m->name,argv[3]);
  1211.         /* Try to find the privileges of this user from the userfile */
  1212.         if((m->privs = userlogin(m->name,NULLCHAR,&m->path,MBXLINE,
  1213.                      &anony)) == -1)
  1214.              if((m->privs = userlogin("bbs",NULLCHAR,&m->path,
  1215.                       MBXLINE,&anony)) == -1)
  1216.               if((m->privs = userlogin("anonymous",NULLCHAR,
  1217.                        &m->path,MBXLINE,&anony)) == -1){
  1218.                         m->privs = 0;
  1219.                         free(m->path);
  1220.                         m->path = NULLCHAR;
  1221.               }
  1222.         tprintf("Oh, hello %s.\n",m->name);
  1223.         if(m->privs & EXCLUDED_CMD)
  1224.             return domboxbye(0,NULLCHARP,p);
  1225.         changearea(m,m->name);
  1226.         return 0;
  1227.     }
  1228.     if(argc > 1 && (m->sid & MBX_SID))    /* "*** Done" or similar */
  1229.         return 2;
  1230.     return -1;
  1231. }
  1232.  
  1233. static int
  1234. doarea(argc,argv,p)
  1235. int argc;
  1236. char *argv[];
  1237. void *p;
  1238. {
  1239.     struct mbx *m;
  1240.     FILE *fp;
  1241.  
  1242.     m = (struct mbx *) p;
  1243.     if(argc < 2){
  1244.         tprintf("Current message area is: %s\n",m->area);
  1245.         tprintf("Available areas are:\n%-15s  Your private mail area\n",
  1246.           m->name);
  1247.         if((fp = fopen(Arealist,READ_TEXT)) == NULLFILE)
  1248.             return 0;
  1249.         sendfile(fp,m->user,ASCII_TYPE,0,0);
  1250.         fclose(fp);
  1251.         return 0;
  1252.     }
  1253.     if((m->privs & SYSOP_CMD) || strcmp(m->name,argv[1]) == 0){
  1254.         changearea(m,argv[1]);
  1255.         if(m->nmsgs){
  1256.             if(!strcmp(m->name,m->area))
  1257.                 tprintf("You have ");
  1258.             else
  1259.                 tprintf("%s: ",m->area);
  1260.             tprintf("%d message%s -  %d new.\n", m->nmsgs,
  1261.               m->nmsgs == 1 ? " " : "s ", m->newmsgs);
  1262.         }
  1263.         return 0;
  1264.     }
  1265.     if(isarea(argv[1])) {
  1266.         changearea(m,argv[1]);
  1267.         tprintf("%s: %d message%s.\n", m->area, m->nmsgs,
  1268.           m->nmsgs == 1 ? "" : "s");
  1269.     }
  1270.     else
  1271.         tprintf("No such message area: %s\n",argv[1]);
  1272.     return 0;
  1273. }
  1274.  
  1275. /* subroutine to do the actual switch from one area to another */
  1276. void
  1277. changearea(m,area)
  1278. struct mbx *m;
  1279. char *area;
  1280. {
  1281.     closenotes(m);
  1282.     m->nmsgs = m->newmsgs = m->current = 0;
  1283.     strcpy(m->area,area);
  1284.     scanmail(m);
  1285. }
  1286.  
  1287. static int
  1288. dombtelnet(argc,argv,p)
  1289. int argc;
  1290. char *argv[];
  1291. void *p;
  1292. {
  1293.     struct mbx *m;
  1294.     int s, len, i;
  1295.     char dsocket[MAXSOCKSIZE];
  1296.     struct sockaddr_in fsocket;
  1297.  
  1298.     m = (struct mbx *) p;
  1299.     fsocket.sin_family = AF_INET;
  1300.     if(argc < 3)
  1301.         fsocket.sin_port = IPPORT_TELNET;
  1302.     else
  1303.         fsocket.sin_port = atoi(argv[2]);
  1304.  
  1305.     if((fsocket.sin_addr.s_addr = resolve(argv[1])) == 0){
  1306.         tprintf(Badhost,argv[1]);
  1307.         return 0;
  1308.     }
  1309.     /* Only local telnets are are allowed to the unprivileged user */
  1310.     if(!(m->privs & TELNET_CMD) && !ismyaddr(fsocket.sin_addr.s_addr)){
  1311.         tprintf(Noperm);
  1312.         return 0;
  1313.     }
  1314.     if((s = socket(AF_INET,SOCK_STREAM,0)) == -1){
  1315.         tprintf(Nosock);
  1316.         return 0;
  1317.     }
  1318.     if(fsocket.sin_port == IPPORT_TTYLINK) {
  1319.         m->startmsg = mallocw(80);
  1320.         len = MAXSOCKSIZE;
  1321.         i = getpeername(m->user,dsocket,&len);
  1322.         sprintf(m->startmsg,"*** Incoming call from %s@%s ***\n",
  1323.             m->name,i != -1 ? psocket(dsocket): Hostname);
  1324.     }
  1325.     return gw_connect(m,s,(char *)&fsocket,SOCKSIZE);
  1326. }
  1327.  
  1328. static int
  1329. dombfinger(argc,argv,p)
  1330. int argc;
  1331. char *argv[];
  1332. void *p;
  1333. {
  1334.     struct mbx *m;
  1335.     char *host, *user = NULLCHAR, buf[8], *newargv[3];
  1336.  
  1337.     if(argc > 2){
  1338.         tprintf("Usage: F user@host  or  F @host  or  F user.\n");
  1339.         return 0;
  1340.     }
  1341.     host = Hostname;
  1342.     if(argc == 2){
  1343.         if((host = strchr(argv[1], '@')) != NULLCHAR){
  1344.             *host = '\0';
  1345.             host++;
  1346.         } else
  1347.             host = Hostname;
  1348.         user = argv[1];
  1349.     }
  1350.     m = (struct mbx *) p;
  1351.     m->startmsg = mallocw(80);
  1352.     if(user != NULLCHAR)
  1353.         sprintf(m->startmsg,"%s\n",user);
  1354.     else
  1355.         strcpy(m->startmsg,"\n");
  1356.     newargv[0] = "telnet";
  1357.     newargv[1] = host;
  1358.     sprintf(buf,"%d",IPPORT_FINGER);
  1359.     newargv[2] = buf;
  1360.     return dombtelnet(3,newargv,p);
  1361. }
  1362.  
  1363. /* Generic mbox gateway code. It sends and frees the contents of m->startmsg
  1364.  * when the connection has been established unless it a null pointer.
  1365.  */
  1366. int
  1367. gw_connect(m,s,fsocket,len)
  1368. struct mbx *m;
  1369. int s;
  1370. char *fsocket;
  1371. int len;
  1372. {
  1373.     int c;
  1374.     char *cp;
  1375.     struct proc *child;
  1376.     struct gwalarm *gwa;
  1377.  
  1378.     sockmode(s,SOCK_ASCII);
  1379.     child = newproc("gateway supervisor",256,gw_superv,0,Curproc,m,0);
  1380.     tprintf("Trying %s...  ",psocket((struct sockaddr *)fsocket));
  1381.     dombescape(0,NULLCHARP,(void *)m);
  1382.     usflush(Curproc->output);
  1383.     if(connect(s,fsocket,len) == -1){
  1384.         cp = sockerr(s);
  1385.         tprintf("Connection failed: ");
  1386.         if(cp != NULLCHAR)
  1387.             tprintf("%s errno %d\n",cp,errno);
  1388.         else
  1389.             tprintf("Escape character sent.\n");
  1390.         free(m->startmsg);
  1391.         m->startmsg = NULLCHAR;
  1392.         killproc(child);
  1393.         close_s(s);
  1394.         return 0;
  1395.     }
  1396.     /* The user did not type the escape character */
  1397.     killproc(child);
  1398.     tputs("Connected.\n");
  1399.     
  1400.     if(m->startmsg != NULLCHAR){
  1401.         usputs(s,m->startmsg);
  1402.         free(m->startmsg);
  1403.         m->startmsg = NULLCHAR;
  1404.     }
  1405.     /* Since NOS does not flush the output socket after a certain
  1406.      * period of time, we have to arrange that ourselves.
  1407.      */
  1408.     gwa = (struct gwalarm *) mallocw(sizeof(struct gwalarm));
  1409.     gwa->s1 = Curproc->output;
  1410.     gwa->s2 = s;
  1411.     set_timer(&gwa->t,240L);
  1412.     gwa->t.func = gw_alarm;
  1413.     gwa->t.arg = (void *) gwa;
  1414.     start_timer(&gwa->t);
  1415.     /* Fork off the receive process */
  1416.     child = newproc("gateway in",1024,gw_input,s,NULL,Curproc,0);
  1417.     
  1418.     for(;;){
  1419.         if((c = recvchar(Curproc->input)) == EOF)
  1420.             break;
  1421.         if(c == m->escape){
  1422.             tputs("Disconnecting.");
  1423.             if(socklen(Curproc->input,0))
  1424.                 recv_mbuf(Curproc->input,NULL,0,NULLCHAR,0);
  1425.             break;
  1426.         }
  1427.         if(usputc(s,(char)c) == EOF)
  1428.             break;
  1429.     }
  1430.     stop_timer(&gwa->t);
  1431.     free((char *)gwa);
  1432.     close_s(s);
  1433.     killproc(child); /* get rid of the receive process */
  1434.     tprintf("%c%c%c\n",IAC,WONT,TN_ECHO);
  1435.     return 0;
  1436. }
  1437.  
  1438. static void
  1439. __stdargs gw_input(s,notused,p)
  1440. int s;
  1441. void *notused;
  1442. void *p;
  1443. {
  1444.     int c;
  1445.     char *cp;
  1446.     struct proc *parent;
  1447.  
  1448.     parent = (struct proc *) p;
  1449.     while((c = recvchar(s)) != EOF)
  1450.         tputc((char)c);
  1451.     tprintf("Disconnected ");
  1452.     cp = sockerr(s);
  1453.     if(cp != NULLCHAR)
  1454.         tputs(cp);
  1455.     /* Tell the parent that we are no longer connected */
  1456.     alert(parent,ENOTCONN);
  1457.     pwait(Curproc); /* Now wait to be killed */
  1458. }
  1459.  
  1460. /* Check if the escape character is typed while the parent process is busy
  1461.  * doing other things. 
  1462.  */
  1463. static void
  1464. __stdargs gw_superv(null,proc,p)
  1465. int null;
  1466. void *proc;
  1467. void *p;
  1468. {
  1469.     struct proc *parent;
  1470.     struct mbx *m;
  1471.     int c;
  1472.     parent = (struct proc *) proc;
  1473.     m = (struct mbx *) p;
  1474.     while((c = recvchar(Curproc->input)) != EOF)
  1475.         if(c == m->escape){
  1476.             /* flush anything in the input queue */
  1477.             if(socklen(Curproc->input,0))
  1478.                 recv_mbuf(Curproc->input,NULL,0,NULLCHAR,0);
  1479.             break;
  1480.         }
  1481.     alert(parent,EINTR);     /* Tell the parent to quit */
  1482.     pwait(Curproc);         /* Please kill me */
  1483. }
  1484.  
  1485. static void
  1486. gw_alarm(p)
  1487. void *p;
  1488. {
  1489.     struct gwalarm *gwa = (struct gwalarm *)p;
  1490.     char oldbl;
  1491.     struct usock *up;
  1492.  
  1493.     /* Flush sockets s1 and s2, but first make sure that the socket
  1494.      * is set to non-blocking mode, to prevent the flush from blocking
  1495.      * if the high water mark has been reached.
  1496.      */
  1497.     if((up = itop(gwa->s1)) != NULLUSOCK) {
  1498.         oldbl = up->noblock;
  1499.         up->noblock = 1;
  1500.         usflush(gwa->s1);
  1501.         up->noblock = oldbl;
  1502.     }
  1503.     if((up = itop(gwa->s2)) != NULLUSOCK) {
  1504.         oldbl = up->noblock;
  1505.         up->noblock = 1;
  1506.         usflush(gwa->s2);
  1507.         up->noblock = oldbl;
  1508.     }
  1509.     start_timer(&gwa->t);
  1510. }
  1511.  
  1512. /* States for send line parser state machine */
  1513. #define        LOOK_FOR_USER        2
  1514. #define        IN_USER            3
  1515. #define        AFTER_USER        4
  1516. #define        LOOK_FOR_HOST        5
  1517. #define        IN_HOST            6
  1518. #define        AFTER_HOST        7
  1519. #define        LOOK_FOR_FROM        8
  1520. #define        IN_FROM            9
  1521. #define        AFTER_FROM        10
  1522. #define        LOOK_FOR_MSGID        11
  1523. #define        IN_MSGID        12
  1524. #define        FINAL_STATE        13
  1525. #define        ERROR_STATE        14
  1526.  
  1527. /* Prepare the addressee.  If the address is bad, return -1, otherwise
  1528.  * return 0
  1529.  */
  1530. static int
  1531. mbx_to(argc,argv,p)
  1532. int argc;
  1533. char *argv[];
  1534. void *p;
  1535. {
  1536.     register char *cp;
  1537.     int state, i;
  1538.     char *user, *host, *from, *msgid;
  1539.     int userlen = 0, hostlen = 0, fromlen = 0, msgidlen = 0;
  1540.     struct mbx *m;
  1541.  
  1542.     m = (struct mbx *)p;
  1543.     /* Free anything that might be allocated
  1544.      * since the last call to mbx_to() or mbx_reply()
  1545.      */
  1546.     free(m->to);
  1547.     m->to = NULLCHAR;
  1548.     free(m->tofrom);
  1549.     m->tofrom = NULLCHAR;
  1550.     free(m->tomsgid);
  1551.     m->tomsgid = NULLCHAR;
  1552.     free(m->origto);
  1553.     m->origto = NULLCHAR;
  1554.  
  1555.     if(argc == 1)
  1556.         return -1;
  1557.     i = 1;
  1558.     cp = argv[i];
  1559.     state = LOOK_FOR_USER;
  1560.     while(state < FINAL_STATE){
  1561. #ifdef MBDEBUG
  1562.         tprintf("State is %d, char is %c\n", state, *cp);
  1563. #endif
  1564.         switch(state){
  1565.         case LOOK_FOR_USER:
  1566.             if(*cp == '@' || *cp == '<' || *cp == '$'){
  1567.                 state = ERROR_STATE;        /* no user */
  1568.             } else {
  1569.                 user = cp;            /* point at start */
  1570.                 userlen++;            /* start counting */
  1571.                 state = IN_USER;
  1572.             }
  1573.             break;
  1574.         case IN_USER:
  1575.             switch(*cp){
  1576.             case '\0':
  1577.                 state = AFTER_USER;        /* done with username */
  1578.                 break;
  1579.             case '@':
  1580.                 state = LOOK_FOR_HOST;        /* hostname should follow */
  1581.                 break;
  1582.             case '<':
  1583.                 state = LOOK_FOR_FROM;        /* from name should follow */
  1584.                 break;
  1585.             case '$':
  1586.                 state = LOOK_FOR_MSGID;    /* message id should follow */
  1587.                 break;
  1588.             default:
  1589.                 userlen++;            /* part of username */
  1590.             }
  1591.             break;
  1592.         case AFTER_USER:
  1593.             switch(*cp){
  1594.             case '@':
  1595.                 state = LOOK_FOR_HOST;        /* hostname follows */
  1596.                 break;
  1597.             case '<':
  1598.                 state = LOOK_FOR_FROM;        /* fromname follows */
  1599.                 break;
  1600.             case '$':
  1601.             state = LOOK_FOR_MSGID;    /* message id follows */
  1602.                 break;
  1603.             default:
  1604.                 state = ERROR_STATE;
  1605.             }
  1606.             break;
  1607.         case LOOK_FOR_HOST:
  1608.             if(*cp == '@' || *cp == '<' || *cp == '$'){
  1609.                 state = ERROR_STATE;
  1610.                 break;
  1611.             }
  1612.             if(*cp == '\0')
  1613.                 break;
  1614.             host = cp;
  1615.             hostlen++;
  1616.             state = IN_HOST;
  1617.             break;
  1618.         case IN_HOST:
  1619.             switch(*cp){
  1620.             case '\0':
  1621.                 state = AFTER_HOST;        /* found user@host */
  1622.                 break;
  1623.             case '@':
  1624.                 state = ERROR_STATE;        /* user@host@? */
  1625.                 break;
  1626.             case '<':
  1627.                 state = LOOK_FOR_FROM;        /* fromname follows */
  1628.                 break;
  1629.             case '$':
  1630.                 state = LOOK_FOR_MSGID;    /* message id follows */
  1631.                 break;
  1632.             default:
  1633.                 hostlen++;
  1634.             }
  1635.             break;
  1636.         case AFTER_HOST:
  1637.             switch(*cp){
  1638.             case '@':
  1639.                 state = ERROR_STATE;        /* user@host @ */
  1640.                 break;
  1641.             case '<':
  1642.                 state = LOOK_FOR_FROM;        /* user@host < */
  1643.                 break;
  1644.             case '$':
  1645.                 state = LOOK_FOR_MSGID;    /* user@host $ */
  1646.                 break;
  1647.             default:
  1648.                 state = ERROR_STATE;        /* user@host foo */
  1649.             }
  1650.             break;
  1651.         case LOOK_FOR_FROM:
  1652.             if(*cp == '@' || *cp == '<' || *cp == '$'){
  1653.                 state = ERROR_STATE;
  1654.                 break;
  1655.             }
  1656.             if(*cp == '\0')
  1657.                 break;
  1658.             from = cp;
  1659.             fromlen++;
  1660.             state = IN_FROM;
  1661.             break;
  1662.         case IN_FROM:
  1663.             switch(*cp){
  1664.             case '\0':
  1665.                 state = AFTER_FROM;        /* user@host <foo */
  1666.                 break;
  1667.             case '<':
  1668.                 state = ERROR_STATE;        /* user@host <foo< */
  1669.                 break;
  1670.             case '$':
  1671.                 state = LOOK_FOR_MSGID;    /* message id follows */
  1672.                 break;
  1673.             default:
  1674.                 fromlen++;
  1675.             }
  1676.             break;
  1677.         case AFTER_FROM:
  1678.             switch(*cp){
  1679.             case '@':                /* user@host <foo @ */
  1680.             case '<':                /* user@host <foo < */
  1681.                 state = ERROR_STATE;
  1682.                 break;
  1683.             case '$':
  1684.                 state = LOOK_FOR_MSGID;    /* user@host <foo $ */
  1685.                 break;
  1686.             default:
  1687.                 state = ERROR_STATE;        /* user@host foo */
  1688.             }
  1689.             break;
  1690.         case LOOK_FOR_MSGID:
  1691.             if(*cp == '\0')
  1692.                 break;
  1693.             msgid = cp;
  1694.             msgidlen++;
  1695.             state = IN_MSGID;
  1696.             break;
  1697.         case IN_MSGID:
  1698.             if(*cp == '\0')
  1699.                 state = FINAL_STATE;
  1700.             else
  1701.                 msgidlen++;
  1702.             break;
  1703.         default:
  1704.             /* what are we doing in this state? */
  1705.             state = ERROR_STATE;
  1706.         }
  1707.         if(*(cp) == '\0'){
  1708.             ++i;
  1709.             if(i < argc)
  1710.             cp = argv[i];
  1711.             else break;
  1712.         } else
  1713.             ++cp;
  1714.     }
  1715.     if(state == ERROR_STATE || state == LOOK_FOR_HOST
  1716.      || state == LOOK_FOR_FROM || state == LOOK_FOR_MSGID)
  1717.         return -1;        /* syntax error */
  1718.  
  1719.     m->to = mallocw(userlen + hostlen + 2);
  1720.  
  1721.     strncpy(m->to, user, userlen);
  1722.     m->to[userlen] = '\0';
  1723.  
  1724.     if(hostlen){
  1725.         m->to[userlen] = '@';
  1726.         strncpy(m->to + userlen + 1, host, hostlen);
  1727.         m->to[userlen + hostlen + 1] = '\0';
  1728.     }
  1729.     if(fromlen){
  1730.         m->tofrom = mallocw(fromlen + 1);
  1731.         strncpy(m->tofrom, from, fromlen);
  1732.         m->tofrom[fromlen] = '\0';
  1733.     }
  1734.     if(msgidlen){
  1735.         m->tomsgid = mallocw(msgidlen + 1);
  1736.         strncpy(m->tomsgid, msgid, msgidlen);
  1737.         m->tomsgid[msgidlen] = '\0';
  1738.     }
  1739.     return 0;
  1740. }
  1741.  
  1742. /* This opens the data file and writes the mail header into it.
  1743.  * Returns 0 if OK, and -1 if not.
  1744.  */
  1745. static int
  1746. mbx_data(m,cclist,extra)
  1747. struct mbx *m;
  1748. struct list *cclist;    /* list of carbon copy recipients */
  1749. char *extra;        /* optional extra header lines */
  1750. {
  1751.     time_t t;
  1752.     struct list *ap;
  1753.     int cccnt = 0;
  1754.     
  1755.     if((m->tfile = tmpfile()) == NULLFILE)
  1756.         return -1;
  1757.     time(&t);
  1758.     fprintf(m->tfile,Hdrs[RECEIVED]);
  1759.     if(m->tofrom != NULLCHAR)
  1760.         fprintf(m->tfile,"from %s.bbs ",m->name);
  1761.     fprintf(m->tfile,"by %s (%s)\n\tid AA%ld ; %s",
  1762.         Hostname, Version, get_msgid(), ptime(&t));
  1763.     fprintf(m->tfile,"%s%s",Hdrs[DATE],ptime(&t));
  1764.     fprintf(m->tfile,Hdrs[MSGID]);
  1765.     if(m->tomsgid)
  1766.         fprintf(m->tfile,"<%s@%s.bbs>\n", m->tomsgid, m->name);
  1767.     else
  1768.         fprintf(m->tfile,"<%ld@%s>\n",get_msgid(), Hostname);
  1769.     fprintf(m->tfile,Hdrs[FROM]);
  1770.     if(m->tofrom)
  1771.         fprintf(m->tfile,"%s%%%s.bbs@%s\n",
  1772.             m->tofrom, m->name, Hostname);
  1773.     else
  1774.         fprintf(m->tfile,"%s@%s\n", m->name, Hostname);
  1775.     fprintf(m->tfile,"%s%s\n",Hdrs[TO],m->origto != NULLCHAR ? m->origto : m->to);
  1776.     /* Write Cc: line */
  1777.     for(ap = cclist; ap != NULLLIST; ap = ap->next) {
  1778.         if(cccnt == 0){
  1779.             fprintf(m->tfile,"%s",Hdrs[CC]);
  1780.             cccnt = 4;
  1781.         }
  1782.         else {
  1783.                fprintf(m->tfile,", ");
  1784.                cccnt += 2;
  1785.         }
  1786.         if(cccnt + strlen(ap->val) > 80 - 3) {
  1787.                fprintf(m->tfile,"\n    ");
  1788.                cccnt = 4;
  1789.         }
  1790.         fputs(ap->val,m->tfile);
  1791.         cccnt += strlen(ap->val);
  1792.     }
  1793.     if(cccnt)
  1794.         fputc('\n',m->tfile);
  1795.     fprintf(m->tfile,"%s%s\n",Hdrs[SUBJECT],m->line);
  1796.     if(!isspace(m->stype) && ((m->stype != 'R' && m->stype != 'F') ||
  1797.       (m->sid & MBX_SID) !=0))
  1798.           fprintf(m->tfile,"%s%c\n", Hdrs[BBSTYPE],m->stype);
  1799.     if(extra != NULLCHAR)
  1800.         fprintf(m->tfile,extra);
  1801.     fprintf(m->tfile,"\n");
  1802.  
  1803.     return 0;
  1804. }
  1805.  
  1806. /* Returns true if string is in history file or if string appears to be a
  1807.  * message id generated by our system.
  1808.  */
  1809. static int
  1810. msgidcheck(string)
  1811. char *string;
  1812. {
  1813.      FILE *fp;
  1814.      char buf[LINELEN], *cp;
  1815.      if(string == NULLCHAR)
  1816.       return 0;
  1817.      /* BID's that we have generated ourselves are not kept in the history
  1818.       * file. Such BID's are in the nnnn_hhhh form, where hhhh is a part of
  1819.       * our hostname, truncated so that the BID is no longer than 11
  1820.       * characters.
  1821.       */
  1822.      if((cp = strchr(string,'_')) != NULLCHAR && *(cp+1) != '\0' && 
  1823.     strnicmp(cp+1,Hostname,strlen(cp+1)) == 0)
  1824.       return 1;
  1825.  
  1826.      if((fp = fopen(Historyfile,READ_TEXT)) == NULLFILE)
  1827.       return 0;
  1828.      while(fgets(buf,LINELEN,fp) != NULLCHAR) {
  1829.       rip(buf);
  1830.       if(stricmp(string,buf) == 0) {    /* found */
  1831.            fclose(fp);
  1832.            return 1;
  1833.       }
  1834.      }
  1835.      fclose(fp);
  1836.      return 0;
  1837. }
  1838.      
  1839. /* Read the rewrite file for lines where the first word is a regular
  1840.  * expression and the second word are rewriting rules. The special
  1841.  * character '$' followed by a digit denotes the string that matched
  1842.  * a '*' character. The '*' characters are numbered from 1 to 9.
  1843.  * Example: the line "*@*.* $2@$1.ampr.org" would rewrite the address
  1844.  * "foo@bar.xxx" to "bar@foo.ampr.org".
  1845.  * $H is replaced by our hostname, and $$ is an escaped $ character.
  1846.  * If the third word on the line has an 'r' character in it, the function
  1847.  * will recurse with the new address.
  1848.  */
  1849. char *
  1850. rewrite_address(addr)
  1851. char *addr;
  1852. {
  1853.     char *argv[10], buf[MBXLINE], *cp, *cp2, *retstr;
  1854.     int cnt;
  1855.     FILE *fp;
  1856.     if ((fp = fopen(Rewritefile,READ_TEXT)) == NULLFILE)
  1857.         return NULLCHAR;
  1858.     memset((char *)argv,0,10*sizeof(char *));
  1859.     while(fgets(buf,MBXLINE,fp) != NULLCHAR) {
  1860.         if(*buf == '#')        /* skip commented lines */
  1861.             continue;
  1862.         string_tidy(buf);   /* clean up the buffer */
  1863.         if((cp = strchr(buf,' ')) == NULLCHAR) /* get the first word */
  1864.             if((cp = strchr(buf,'\t')) == NULLCHAR)
  1865.                 continue;
  1866.         *cp = '\0';
  1867.         if((cp2 = strchr(buf,'\t')) != NULLCHAR){
  1868.             *cp = ' ';
  1869.             cp = cp2;
  1870.             *cp = '\0';
  1871.         }
  1872.         if(!wildmat(addr,buf,argv))
  1873.             continue;        /* no match */
  1874.         rip(++cp);
  1875.         cp2 = retstr = (char *) callocw(1,MBXLINE);
  1876.         while(*cp != '\0' && *cp != ' ' && *cp != '\t')
  1877.             if(*cp == '$') {
  1878.                 if(isdigit(*(++cp)))
  1879.                     if(argv[*cp - '0'-1] != '\0')
  1880.                         strcat(cp2,argv[*cp - '0'-1]);
  1881.                 if(*cp == 'h' || *cp == 'H') /* Our hostname */
  1882.                     strcat(cp2,Hostname);
  1883.                 if(*cp == '$')    /* Escaped $ character */
  1884.                     strcat(cp2,"$");
  1885.                 cp2 = retstr + strlen(retstr);
  1886.                 cp++;
  1887.             }
  1888.             else
  1889.                 *cp2++ = *cp++;
  1890.         for(cnt=0; argv[cnt] != NULLCHAR; ++cnt)
  1891.             free(argv[cnt]);
  1892.         fclose(fp);
  1893.         /* If there remains an 'r' character on the line, repeat
  1894.          * everything by recursing.
  1895.          */
  1896.         if(strchr(cp,'r') != NULLCHAR || strchr(cp,'R') != NULLCHAR) {
  1897.             if((cp2 = rewrite_address(retstr)) != NULLCHAR) {
  1898.                 free(retstr);
  1899.                 return cp2;
  1900.             }
  1901.         }
  1902.         return retstr;
  1903.     }
  1904.     fclose(fp);
  1905.     return NULLCHAR;
  1906. }
  1907.  
  1908. /* uuencode a file -- translated from C++; both versions copyright 1990
  1909.    by David R. Evans, G4AMJ/NQ0I
  1910. */
  1911.  
  1912. static int
  1913. uuencode(infile,s,infilename)
  1914. FILE *infile;
  1915. int s;            /* output socket */
  1916. char *infilename;
  1917. {
  1918.   unsigned long cnt = 0L;            /* 11 Mar 92  GT  BC3 couldn't cope.    */
  1919.   int n_read_so_far = 0;
  1920.   int n_written_so_far = 0;
  1921.   int in_chars;
  1922.   int n;
  1923.   int mode = 0755;
  1924.   unsigned char in[3], out[4], line[100];
  1925. #ifdef UNIX
  1926.   struct stat stb;
  1927.   
  1928.   if(stat(infilename,&stb) != -1)
  1929.        mode = stb.st_mode & 0777;    /* get real file protection mode */
  1930. #endif
  1931.  
  1932.   usprintf(s, "begin %03o %s\n", mode, infilename);
  1933.  
  1934.   /* do the encode */
  1935.   for(;;) {
  1936.     in_chars = fread(in, 1, 3, infile);
  1937.     out[0] = in[0] >> 2;
  1938.     out[1] = in[0] << 6;
  1939.     out[1] = out[1] >> 2;
  1940.     out[1] = out[1] | (in[1] >> 4);
  1941.     out[2] = in[1] << 4;
  1942.     out[2] = out[2] >> 2;
  1943.     out[2] = out[2] | (in[2] >> 6);
  1944.     out[3] = in[2] << 2;
  1945.     out[3] = out[3] >> 2;
  1946.     for (n = 0; n < 4; n++)
  1947.       out[n] += ' ';
  1948.     n_read_so_far += in_chars;
  1949.     for (n = 0; n < 4; n++)
  1950.       line[n_written_so_far++] = out[n];
  1951.     if (((in_chars != 3) || (n_written_so_far == 60)) && n_read_so_far > 0) {
  1952.       line[(n_read_so_far + 2) / 3 * 4] = '\0';
  1953.       
  1954.       usprintf(s,"%c%s\n",n_read_so_far + ' ', line);
  1955.       cnt += n_read_so_far;
  1956.       n_read_so_far = 0;
  1957.       n_written_so_far = 0;
  1958.     }
  1959.     if (in_chars == 0)
  1960.       break;
  1961.   }
  1962.   if (usprintf(s," \nend\nsize %lu\n", cnt) == EOF)
  1963.     return 1;
  1964.   return 0;
  1965. }
  1966.  
  1967. /* Added by paul@wolf.demon.co.uk - Check to see if 'name' contains
  1968.  * a bang path, a mail redirector, or a host name != Hostname
  1969.  *  Returns 1 if so, 0 if the name is OK
  1970.  */
  1971.  
  1972. static int
  1973. check_mail_privilege (name)
  1974. char *name;
  1975. {
  1976.     char    *host;
  1977.  
  1978.     if ((strchr(name,'!') != NULLCHAR ) || ( strchr(name,'%') != NULLCHAR))
  1979.         return 1;
  1980.     if ((host = strrchr(name,'@')) == NULLCHAR)
  1981.         return 0;
  1982.     host++;
  1983.     if (stricmp(host,Hostname) != 0)
  1984.         return 1;
  1985.  
  1986.     return 0;
  1987. }
  1988.  
  1989. /* string_tidy - reduce runs of whitespace to 1 space */
  1990. static void string_tidy(char *str)   
  1991. {
  1992. char *ip=str;
  1993. char *op=str;
  1994. int c;
  1995. while( c = *ip++)
  1996.   {
  1997.     if( c!=' ' && c != '\t' ) {
  1998.       *op++ = c;
  1999.       continue;
  2000.     }
  2001.     *op++=' ';
  2002.     while( *ip && (*ip == ' ' || *ip =='\t') ) ip++ ;
  2003.   }
  2004.   *op='\0';  
  2005. }
  2006.